<!DOCTYPE html>

<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Speech Synthesis Tester</title>
  <style>
* {
  box-sizing: border-box;
}

body {
font-family: Helvetica, Arial, sans-serif;
max-width: 600px;
margin: 0 auto;
padding: 20px;
background-color: #f5f5f5;
}

.container {
background: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}

h1 {
color: #333;
margin-bottom: 20px;
text-align: center;
}

.form-group {
margin-bottom: 20px;
}

label {
display: block;
margin-bottom: 8px;
font-weight: bold;
color: #555;
}

textarea {
width: 100%;
padding: 12px;
border: 2px solid #ddd;
border-radius: 4px;
font-size: 16px;
font-family: Helvetica, Arial, sans-serif;
resize: vertical;
min-height: 100px;
}

select {
width: 100%;
padding: 12px;
border: 2px solid #ddd;
border-radius: 4px;
font-size: 16px;
font-family: Helvetica, Arial, sans-serif;
background: white;
}

.controls {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 15px;
margin-bottom: 20px;
}

.control-group {
display: flex;
flex-direction: column;
}

.control-group label {
font-size: 14px;
margin-bottom: 5px;
}

input[type=“range”] {
width: 100%;
margin: 5px 0;
}

.range-value {
text-align: center;
font-size: 12px;
color: #666;
}

.buttons {
display: flex;
gap: 10px;
justify-content: center;
}

button {
padding: 12px 24px;
font-size: 16px;
font-family: Helvetica, Arial, sans-serif;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.2s;
}

.speak-btn {
background-color: #007bff;
color: white;
}

.speak-btn:hover {
background-color: #0056b3;
}

.speak-btn:disabled {
background-color: #6c757d;
cursor: not-allowed;
}

.stop-btn {
background-color: #dc3545;
color: white;
}

.stop-btn:hover {
background-color: #c82333;
}

.status {
margin-top: 15px;
padding: 10px;
border-radius: 4px;
text-align: center;
font-weight: bold;
}

.status.speaking {
background-color: #d4edda;
color: #155724;
}

.status.stopped {
background-color: #f8d7da;
color: #721c24;
}

.status.ready {
background-color: #d1ecf1;
color: #0c5460;
}
</style>

</head>
<body>
  <div class="container">
    <h1>Speech synthesis tester</h1>

<div class="form-group">
  <label for="textInput">Text to speak:</label>
  <textarea id="textInput" placeholder="Enter text to be spoken...">Hello, this is a test of the speech synthesis API!</textarea>
</div>

<div class="form-group">
  <label for="voiceSelect">Voice:</label>
  <select id="voiceSelect">
    <option value="">Loading voices...</option>
  </select>
</div>

<div class="controls">
  <div class="control-group">
    <label for="rateRange">Rate:</label>
    <input type="range" id="rateRange" min="0.1" max="3" step="0.1" value="1">
    <div class="range-value" id="rateValue">1.0</div>
  </div>
  
  <div class="control-group">
    <label for="pitchRange">Pitch:</label>
    <input type="range" id="pitchRange" min="0" max="2" step="0.1" value="1">
    <div class="range-value" id="pitchValue">1.0</div>
  </div>
  
  <div class="control-group">
    <label for="volumeRange">Volume:</label>
    <input type="range" id="volumeRange" min="0" max="1" step="0.1" value="1">
    <div class="range-value" id="volumeValue">1.0</div>
  </div>
</div>

<div class="buttons">
  <button class="speak-btn" id="speakBtn">Speak</button>
  <button class="stop-btn" id="stopBtn">Stop</button>
</div>

<div class="status ready" id="status">Ready to speak</div>

  </div>

  <script type="module">
let currentUtterance = null;
let voices = [];

// DOM elements
const textInput = document.getElementById('textInput');
const voiceSelect = document.getElementById('voiceSelect');
const rateRange = document.getElementById('rateRange');
const pitchRange = document.getElementById('pitchRange');
const volumeRange = document.getElementById('volumeRange');
const rateValue = document.getElementById('rateValue');
const pitchValue = document.getElementById('pitchValue');
const volumeValue = document.getElementById('volumeValue');
const speakBtn = document.getElementById('speakBtn');
const stopBtn = document.getElementById('stopBtn');
const status = document.getElementById('status');

// Load available voices
function loadVoices() {
  voices = speechSynthesis.getVoices();
  voiceSelect.innerHTML = '';
  
  if (voices.length === 0) {
    voiceSelect.innerHTML = '<option value="">No voices available</option>';
    return;
  }
  
  // Add default option
  const defaultOption = document.createElement('option');
  defaultOption.value = '';
  defaultOption.textContent = 'Default voice';
  voiceSelect.appendChild(defaultOption);
  
  // Add all available voices
  voices.forEach((voice, index) => {
    const option = document.createElement('option');
    option.value = index;
    option.textContent = `${voice.name} (${voice.lang})`;
    voiceSelect.appendChild(option);
  });
}

// Update range value displays
function updateRangeValues() {
  rateValue.textContent = rateRange.value;
  pitchValue.textContent = pitchRange.value;
  volumeValue.textContent = volumeRange.value;
}

// Update status display
function updateStatus(message, className) {
  status.textContent = message;
  status.className = `status ${className}`;
}

// Speak function
function speak() {
  const text = textInput.value.trim();
  
  if (!text) {
    updateStatus('Please enter some text to speak', 'ready');
    return;
  }
  
  // Stop any current speech
  speechSynthesis.cancel();
  
  // Create new utterance
  currentUtterance = new SpeechSynthesisUtterance();
  currentUtterance.text = text;
  currentUtterance.rate = parseFloat(rateRange.value);
  currentUtterance.pitch = parseFloat(pitchRange.value);
  currentUtterance.volume = parseFloat(volumeRange.value);
  
  // Set voice if selected
  const selectedVoiceIndex = voiceSelect.value;
  if (selectedVoiceIndex && voices[selectedVoiceIndex]) {
    currentUtterance.voice = voices[selectedVoiceIndex];
  }
  
  // Event handlers
  currentUtterance.onstart = () => {
    updateStatus('Speaking...', 'speaking');
    speakBtn.disabled = true;
  };
  
  currentUtterance.onend = () => {
    updateStatus('Speech completed', 'ready');
    speakBtn.disabled = false;
    currentUtterance = null;
  };
  
  currentUtterance.onerror = (event) => {
    updateStatus(`Error: ${event.error}`, 'stopped');
    speakBtn.disabled = false;
    currentUtterance = null;
  };
  
  // Start speaking
  speechSynthesis.speak(currentUtterance);
}

// Stop function
function stop() {
  speechSynthesis.cancel();
  updateStatus('Speech stopped', 'stopped');
  speakBtn.disabled = false;
  currentUtterance = null;
}

// Event listeners
speakBtn.addEventListener('click', speak);
stopBtn.addEventListener('click', stop);

// Update range displays when values change
rateRange.addEventListener('input', updateRangeValues);
pitchRange.addEventListener('input', updateRangeValues);
volumeRange.addEventListener('input', updateRangeValues);

// Load voices when available
speechSynthesis.onvoiceschanged = loadVoices;

// Initial setup
loadVoices();
updateRangeValues();

// Handle browser compatibility
if (!('speechSynthesis' in window)) {
  updateStatus('Speech synthesis not supported in this browser', 'stopped');
  speakBtn.disabled = true;
}
  </script>

</body>
</html>
